<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    const obj = {
      a1: {n1: 2},
      a2: [1],
      // a3: function () {},
      // a4: 4
    }
    obj.a1.m = obj.a2
    obj.a2[1] = obj.a1

    // const obj2 = JSON.parse(JSON.stringify(obj))
    // console.log(obj2)

    // 得到数据类型
    function getType(target) {
      return Object.prototype.toString.call(target).slice(8, -1);
    }

    function cloneDeep(target) {
      // debugger
      let cloneTarget = null
      const type = getType(target)
      if (type==='Object') {
        cloneTarget = {}
      } else if (type==='Array') {
        cloneTarget = []
      } else {
        return target
      }
      // 遍历target所有属性, 添加到cloneTarget中
      for (const key in target) {
        if (Object.hasOwnProperty.call(target, key)) {
          // cloneTarget[key] = target[key];  // 浅拷贝
          cloneTarget[key] = cloneDeep(target[key])  //.递归调用实现深拷贝
        }
      }

      return cloneTarget
    }

    // 一个对象只需要被拷贝一次, 如果后面又需要, 直接返回前面拷贝的对象
    function cloneDeep(target, map=new Map()) {
      // debugger
      // 从缓存中读取拷贝对象
      let cloneTarget = map.get(target)
      // 如果有, 直接返回它
      if (cloneTarget) return cloneTarget
      const type = getType(target)
      if (type==='Object') {
        cloneTarget = {} 
      } else if (type==='Array') {
        cloneTarget = []
      } else {
        return target
      }
      // 缓存新的拷贝对象
      map.set(target, cloneTarget)
      // 遍历target所有属性, 添加到cloneTarget中
      for (const key in target) {
        if (Object.hasOwnProperty.call(target, key)) {
          // cloneTarget[key] = target[key];  // 浅拷贝
          cloneTarget[key] = cloneDeep(target[key], map)  //.递归调用, 传递map
        }
      }

      return cloneTarget
    }

    const obj2 = cloneDeep(obj)
    console.log(obj2, obj2.a1===obj.a1)
  </script>
</body>
</html>